大家好 ~ 今天是自我挑戰的第28天!今天要分享的是我所有的程式碼!
首先先介紹我的檔案內容文件:
oracle-test/
├── backend/
│ ├── middleware/
│ │ └── auth.js
│ ├── routes/
│ │ ├── auth.js
│ │ ├── patients.js
│ │ └── records.js
│ ├── database.js
│ ├── package-lock.json
│ ├── package.json
│ └── server.js
├── frontend/
│ ├── index.html ✅
│ ├── patients.html ✅
│ ├── patients.js ✅
│ ├── records.css ✅
│ ├── records.html ✅
│ ├── records.js ✅
│ ├── script.js ✅
│ └── style.css ✅
├── .env
├── server.js
├── test-oracle11g.js
└── test.js
index.html
<!-- 使用者打開網頁時第一個看到的頁面,讓醫護人員可以登入或註冊帳號 -->
<!DOCTYPE html>
<html lang="zh-Hant">
<!-- 定義網頁基本資訊(如編碼、viewport以適應不同裝置)、頁面標題(Login/Register) -->
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login/Register</title>
<!-- 引入style.css檔案,美化頁面的外觀 -->
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- 導覽列(包含多個<button>,有"Login"、"Patients"、"Records") -->
<nav class="navbar">
<!-- class="active":表示目前停留在此頁面,CSS會有特別的樣式 -->
<button class="active">Login</button>
<button>Patients</button>
<button>Records</button>
</nav>
<!-- 主區塊(使用Flexbox佈局將內容垂直置中) -->
<div class="container">
<!-- 顯示一個使用者頭像的圖示 -->
<div class="avatar">
<svg class="user-icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 12C14.2091 12 16 10.2091 16 8C16 5.79086 14.2091 4 12 4C9.79086 4 8 5.79086 8 8C8 10.2091 9.79086 12 12 12Z" fill="#666"/>
<path d="M12 14C9.33 14 4 15.34 4 18V19C4 19.55 4.45 20 5 20H19C19.55 20 20 19.55 20 19V18C20 15.34 14.67 14 12 14Z" fill="#666"/>
</svg>
</div>
<!-- 登入表單區塊 -->
<div class="forms">
<div class="form login">
<!-- 表單標題 -->
<h2>登入</h2>
<label>使用者ID</label>
<input type="text" id="loginId" placeholder="請輸入醫療人員ID">
<!-- 用來顯示ID或密碼的錯誤訊息 -->
<div class="error" id="loginIdError"></div>
<label>使用者密碼</label>
<input type="password" id="loginPwd" placeholder="請輸入密碼(需與ID相同)">
<div class="error" id="loginPwdError"></div>
<!-- 觸發登入邏輯的按鈕 -->
<button id="loginBtn">登入</button>
<div class="success-message" id="loginSuccess"></div>
<!-- 點擊後會彈出註冊視窗的按鈕 -->
<button id="showRegisterBtn" style="margin-top: 10px; background-color: #6c757d;">前往註冊</button>
</div>
</div>
</div>
<!-- 註冊彈出視窗 -->
<div id="registerModal" class="modal">
<div class="modal-content">
<div class="form register">
<h2>註冊</h2>
<label>使用者ID</label>
<input type="text" id="regId" placeholder="請輸入醫療人員ID">
<div class="error" id="regIdError"></div>
<label>使用者密碼</label>
<input type="password" id="regPwd" placeholder="密碼將自動設置為您的ID" readonly>
<div class="error" id="regPwdError"></div>
<div class="modal-buttons">
<button id="registerBtn">註冊</button>
<button id="cancelRegisterBtn">取消</button>
</div>
<div class="success-message" id="registerSuccess"></div>
</div>
</div>
</div>
<!-- 引入script.js檔案 -->
<script src="script.js"></script>
</body>
</html>
script.js
// 儲存了所有後端有效的醫療人員ID列表
const validMedicalIds = [
"35102", "36215", "37488", "40756", "41839", "42922",
"45188", "48737", "49920", "51103", "52286", "53469",
"54652", "57018", "58101", "59284", "61650"
];
// 醫療人員角色對照表
const medicalRoles = {
"36215": "doctor", "40756": "doctor", "45188": "doctor",
"49920": "doctor", "54652": "doctor", "59284": "doctor",
"35102": "nurse", "37488": "nurse", "42922": "nurse",
"51103": "nurse", "53469": "nurse", "58101": "nurse",
"41839": "pharmacist", "48737": "pharmacist", "57018": "pharmacist", "61650": "pharmacist",
"52286": "therapist"
};
// 角色名稱對照表(將英文轉為中文名稱)
const roleNames = {
"doctor": "醫師",
"nurse": "護理師",
"pharmacist": "藥師",
"therapist": "治療師"
};
// 從瀏覽器的localStorage中獲取已註冊的使用者資料
let users = JSON.parse(localStorage.getItem('medicalUsers')) || [];
// 事件監聽器
document.addEventListener('DOMContentLoaded', function() {
// 表單驗證函數
function validateForm(id, pwd, isLogin = false) {
const errors = {
id: '',
pwd: ''
};
// 檢查使用者ID是否為空、是否存在於validMedicalIds(必須是有效的醫療人員ID)
if (!id) {
errors.id = '此處為必填!';
} else if (!validMedicalIds.includes(id)) {
errors.id = '無效的醫療人員ID!';
}
// 檢查密碼是否為空、是否存在於validMedicalIds(必須與ID完全相同)
if (!pwd) {
errors.pwd = '此處為必填!';
} else if (pwd !== id) {
errors.pwd = '密碼需與ID相同!';
}
// 返回一個包含錯誤訊息的物件
return errors;
}
// 顯示錯誤消息和紅框
function showErrors(errors, prefix) {
// 清除所有錯誤樣式
const idInput = document.getElementById(`${prefix}Id`);
const pwdInput = document.getElementById(`${prefix}Pwd`);
if (idInput) idInput.classList.remove('error-border');
if (pwdInput) pwdInput.classList.remove('error-border');
// 設置錯誤消息
const idError = document.getElementById(`${prefix}IdError`);
const pwdError = document.getElementById(`${prefix}PwdError`);
if (idError) idError.textContent = errors.id || '';
if (pwdError) pwdError.textContent = errors.pwd || '';
// 添加紅框樣式
if (errors.id && idInput) {
idInput.classList.add('error-border');
}
if (errors.pwd && pwdInput) {
pwdInput.classList.add('error-border');
}
}
// 控制註冊彈窗和"尚未註冊"提示視窗的顯示
function showNotRegisteredModal() {
const existingModal = document.getElementById('notRegisteredModal');
if (existingModal) {
existingModal.remove();
}
const modal = document.createElement('div');
modal.id = 'notRegisteredModal';
modal.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
`;
const modalContent = document.createElement('div');
modalContent.style.cssText = `
background: white;
padding: 30px;
border-radius: 10px;
text-align: center;
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
max-width: 400px;
width: 90%;
animation: modalAppear 0.3s ease-out;
`;
const messageElement = document.createElement('p');
messageElement.textContent = '您尚未註冊,請先註冊再登入!';
messageElement.style.cssText = `
margin-bottom: 20px;
font-size: 16px;
color: #333;
line-height: 1.5;
`;
const buttonContainer = document.createElement('div');
buttonContainer.style.cssText = `
display: flex;
justify-content: center;
gap: 10px;
`;
const confirmButton = document.createElement('button');
confirmButton.textContent = '前往註冊';
confirmButton.style.cssText = `
padding: 10px 20px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.3s;
`;
confirmButton.addEventListener('mouseenter', function() {
this.style.backgroundColor = '#45a049';
});
confirmButton.addEventListener('mouseleave', function() {
this.style.backgroundColor = '#4CAF50';
});
confirmButton.addEventListener('click', function() {
modal.remove();
showRegisterModal();
});
const cancelButton = document.createElement('button');
cancelButton.textContent = '取消';
cancelButton.style.cssText = `
padding: 10px 20px;
background-color: #6c757d;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.3s;
`;
cancelButton.addEventListener('click', function() {
modal.remove();
});
buttonContainer.appendChild(confirmButton);
buttonContainer.appendChild(cancelButton);
modalContent.appendChild(messageElement);
modalContent.appendChild(buttonContainer);
modal.appendChild(modalContent);
document.body.appendChild(modal);
}
// 顯示註冊視窗
function showRegisterModal() {
const modal = document.getElementById('registerModal');
if (modal) {
modal.style.display = 'flex';
clearForm('reg');
}
}
// 隱藏註冊視窗
function hideRegisterModal() {
const modal = document.getElementById('registerModal');
if (modal) {
modal.style.display = 'none';
}
}
// 清除表單和錯誤樣式
function clearForm(prefix) {
const idInput = document.getElementById(`${prefix}Id`);
const pwdInput = document.getElementById(`${prefix}Pwd`);
if (idInput) idInput.value = '';
if (pwdInput) pwdInput.value = '';
if (idInput) idInput.classList.remove('error-border');
if (pwdInput) pwdInput.classList.remove('error-border');
showErrors({id: '', pwd: ''}, prefix);
}
// 檢查是否為未註冊用戶
function checkIfIdNotRegistered(id) {
return !users.some(u => u.id === id);
}
// 檢查登入憑證
function checkLoginCredentials(id, pwd) {
return users.some(u => u.id === id && u.pwd === pwd);
}
// 獲取使用者角色
function getUserRole(id) {
return medicalRoles[id] || null;
}
// 獲取角色名稱
function getRoleName(role) {
return roleNames[role] || '使用者';
}
// 登入成功後跳轉到 Patients 頁面
function loginSuccess(id) {
const role = getUserRole(id);
const roleName = getRoleName(role);
// 儲存使用者資訊到 localStorage
const userInfo = {
employeeId: id,
role: role,
roleName: roleName
};
localStorage.setItem('userInfo', JSON.stringify(userInfo));
alert(`登入成功!歡迎 ${roleName}!`);
clearForm('login');
// 跳轉到 Patients 頁面
window.location.href = 'patients.html';
}
// === 登入按鈕事件 ===
const loginBtn = document.getElementById('loginBtn');
if (loginBtn) {
loginBtn.addEventListener('click', function() {
const id = document.getElementById('loginId').value;
const pwd = document.getElementById('loginPwd').value;
const errors = validateForm(id, pwd, true);
showErrors(errors, 'login');
if (errors.id || errors.pwd) {
return;
}
// 登入邏輯流程:
// 1. 先檢查ID是否已註冊
if (checkIfIdNotRegistered(id)) {
showNotRegisteredModal();
return;
}
// 2. 檢查密碼是否正確
if (checkLoginCredentials(id, pwd)) {
loginSuccess(id);
} else {
const loginErrors = {
id: '',
pwd: '密碼錯誤,請再試一次'
};
showErrors(loginErrors, 'login');
}
});
}
// === 註冊按鈕事件 ===
const registerBtn = document.getElementById('registerBtn');
if (registerBtn) {
registerBtn.addEventListener('click', function() {
const id = document.getElementById('regId').value;
const pwd = id; // 密碼自動設置為ID
document.getElementById('regPwd').value = pwd;
const errors = validateForm(id, pwd, false);
showErrors(errors, 'reg');
if (!errors.id && !errors.pwd) {
// 檢查ID是否已被使用
const idExists = users.some(u => u.id === id);
if (idExists) {
const duplicateErrors = {
id: 'ID已有人使用!請重新輸入!',
pwd: ''
};
showErrors(duplicateErrors, 'reg');
return;
}
// 如果都沒有錯誤,進行註冊
users.push({ id, pwd });
localStorage.setItem('medicalUsers', JSON.stringify(users));
alert('註冊成功!');
clearForm('reg');
hideRegisterModal();
// 註冊成功後自動填入登入表單
document.getElementById('loginId').value = id;
}
});
}
// 顯示註冊視窗按鈕
const showRegisterBtn = document.getElementById('showRegisterBtn');
if (showRegisterBtn) {
showRegisterBtn.addEventListener('click', function() {
showRegisterModal();
});
}
// 取消註冊按鈕
const cancelRegisterBtn = document.getElementById('cancelRegisterBtn');
if (cancelRegisterBtn) {
cancelRegisterBtn.addEventListener('click', function() {
hideRegisterModal();
});
}
// 點擊視窗外部關閉
const registerModal = document.getElementById('registerModal');
if (registerModal) {
registerModal.addEventListener('click', function(e) {
if (e.target === this) {
hideRegisterModal();
}
});
}
// 導覽按鈕
const navButtons = document.querySelectorAll('.navbar button');
if (navButtons.length > 0) {
navButtons.forEach(button => {
button.addEventListener('click', function() {
document.querySelectorAll('.navbar button').forEach(btn => {
btn.classList.remove('active');
});
this.classList.add('active');
const buttonText = this.textContent;
switch(buttonText) {
case 'Login':
window.location.href = 'index.html';
break;
case 'Patients':
// 檢查是否已登入
const userInfo = localStorage.getItem('userInfo');
if (userInfo) {
window.location.href = 'patients.html';
} else {
alert('請先登入系統!');
window.location.href = 'index.html';
}
break;
case 'Records':
window.location.href = 'records.html';
break;
case 'Schedules':
window.location.href = 'schedules.html';
break;
case 'MedicalResourceUsage':
window.location.href = 'resources.html';
break;
}
});
});
}
// 自動檢查登入狀態,如果已登入則跳轉到 Patients
const userInfo = localStorage.getItem('userInfo');
if (userInfo && window.location.pathname.includes('index.html')) {
window.location.href = 'patients.html';
}
});
style.css
/* 設定整個網站的背景顏色(淺藍色)和預設字體 */
body {
font-family: Arial, sans-serif;
margin: 0;
background-color: #e6f4f7;
}
/* 導覽列樣式:包括背景色、置中對齊 */
.navbar {
background-color: #a3d5e4;
padding: 15px 10px;
display: flex;
justify-content: center;
gap: 20px;
}
/* 設定導覽列按鈕的通用樣式 */
.navbar button {
background: white; /* 按鈕為白色背景 */
border: none;
border-radius: 8px; /* 圓角效果 */
padding: 10px 25px; /* 按鈕內邊距 */
color: #333; /* 文字顏色 */
font-size: 14px;
font-weight: bold; /* 讓文字變粗體 */
cursor: pointer;
transition: all 0.2s ease-in-out;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
/* 為當前頁面的按鈕設定不同樣式 */
.navbar button.active {
color: rgb(0, 0, 0);
}
/* 主容器 */
.container {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 40px;
}
/* 頭像 */
.avatar {
width: 100px;
height: 100px;
border-radius: 50%;
background-color: #ccc;
margin-bottom: 30px;
}
/* 表單區塊 */
.forms {
display: flex;
gap: 50px;
}
.form {
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
width: 250px;
display: flex;
flex-direction: column;
gap: 10px;
}
.form h2 {
text-align: center;
}
.form input {
padding: 8px;
border: 1px solid #ccc;
border-radius: 6px;
}
.form button {
padding: 10px;
border: none;
background-color: #4CAF50;
color: white;
border-radius: 6px;
cursor: pointer;
}
/* 滑鼠移過 */
.form button:hover {
background-color: #3d8b40; /* 稍微深一點的綠色 */
}
/* 點下去 */
#loginBtn:active, #registerBtn:active {
background-color: #3c6e3e; /* 更深的綠色 */
}
button {
padding: 10px 20px;
border: none;
border-radius: 6px;
background-color: #4CAF50; /* 原本顏色 */
color: rgb(0, 0, 0);
cursor: pointer;
transition: background-color 0.2s ease; /* 平滑過渡 */
}
/* 滑鼠移過 */
button:hover {
background-color: #4585d3; /* 稍微深一點 */
}
/* 點下去 */
button:active {
background-color: #1b4d8a; /* 更深的顏色 */
}
.form .error {
color: #e74c3c; /* 紅色 */
font-size: 14px;
min-height: 20px;
margin-top: -5px;
}
/* 錯誤邊框樣式 */
.form input.error-border {
border-color: #e74c3c;
}
/* 彈出視窗的樣式 */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1000;
justify-content: center;
align-items: center;
}
.modal-content {
background-color: white;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
animation: modalAppear 0.3s ease-out;
}
/* 讓彈窗出現時更加流暢 */
@keyframes modalAppear {
from {
opacity: 0;
transform: scale(0.9);
}
to {
opacity: 1;
transform: scale(1);
}
}
.modal-buttons {
display: flex;
gap: 10px;
margin-top: 10px;
}
.modal-buttons button {
flex: 1;
}
#cancelRegisterBtn {
background-color: #6c757d;
}
#cancelRegisterBtn:hover {
background-color: #5a6268;
}
/* 病患頁面專用樣式 */
.patients-container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
/* 提醒框樣式 */
.reminder-box {
background: #fff3cd;
border: 1px solid #ffeaa7;
border-radius: 8px;
padding: 15px;
margin-bottom: 30px;
display: flex;
align-items: flex-start;
gap: 10px;
}
.reminder-icon {
font-size: 24px;
}
.patient-actions-card {
background: white;
border-radius: 8px;
padding: 24px;
box-shadow: 0 2px 10px rgba(0,0,0,0.07);
border: 1px solid #EAECEE;
margin-bottom: 30px;
display: flex;
gap: 30px;
align-items: flex-end; /* 讓內容底部對齊 */
}
.reminder-text {
color: #856404;
line-height: 1.5;
}
/* 搜尋和操作區 - 水平排列 */
.search-operations-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
gap: 20px;
}
/* 搜尋容器 */
.search-container {
display: flex;
gap: 10px;
flex: 1;
max-width: 500px;
}
#searchInput {
flex: 1;
padding: 12px 15px;
border: 2px solid #ddd;
border-radius: 6px;
font-size: 14px;
min-width: 300px;
}
#searchInput:focus {
outline: none;
border-color: #007bff;
}
/* 操作按鈕容器 */
.operations-container {
display: flex;
gap: 10px;
align-items: center;
}
/* 按鈕基礎樣式 */
.btn-search, .btn-refresh, .btn-new, .btn-delete, .btn-edit, .btn-save, .btn-cancel {
padding: 10px 20px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
font-weight: bold;
transition: all 0.2s;
white-space: nowrap;
}
.btn-search {
background: #007bff; color: white; border-radius: 0 6px 6px 0;
}
.btn-search:hover {
background: #0056b3;
}
/* 重新整理按鈕 - 灰色 */
.btn-refresh {
background: #6c757d;
color: white;
}
.btn-refresh:hover {
background: #545b62;
}
.btn-new {
background: #28a745;
color: white;
}
.btn-new:hover {
background: #1e7e34;
}
.btn-delete {
background: #dc3545;
color: white;
}
.btn-delete:hover {
background: #c82333;
}
.btn-edit {
background: #ffc107;
color: #212529;
}
.btn-edit:hover {
background: #e0a800;
}
.btn-save {
background: #28a745;
color: white;
}
.btn-save:hover {
background: #1e7e34;
}
.btn-cancel {
background: #6c757d;
color: white;
}
.btn-cancel:hover {
background: #545b62;
}
/* 病患詳細資料區塊 */
.patient-detail-section {
background: white;
border: 2px solid #e0e0e0;
border-radius: 12px;
margin: 0 auto;
max-width: 1000px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
.detail-header {
background: #a3d5e4;
padding: 20px 30px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #ccc;
}
.detail-header h3 {
margin: 0;
color: #333;
font-size: 20px;
font-weight: bold;
}
.header-actions {
display: flex;
gap: 10px;
}
.detail-content {
padding: 30px;
}
.detail-row {
display: flex;
gap: 25px;
margin-bottom: 25px;
}
.detail-group {
flex: 1;
display: flex;
flex-direction: column;
}
.detail-group.full-width {
flex: 2;
}
.detail-group label {
font-weight: bold;
color: #555;
margin-bottom: 8px;
font-size: 14px;
}
.detail-input, .detail-select, .detail-textarea {
padding: 12px 15px;
border: 1px solid #ddd;
border-radius: 6px;
font-size: 14px;
transition: border-color 0.3s;
font-family: inherit;
}
.detail-input:focus, .detail-select:focus, .detail-textarea:focus {
outline: none;
border-color: #007bff;
}
.detail-input:disabled, .detail-select:disabled, .detail-textarea:disabled {
background-color: #f8f9fa;
color: #6c757d;
cursor: not-allowed;
}
.detail-textarea {
resize: vertical;
min-height: 80px;
}
/* 編輯模式樣式 */
.detail-input.editing, .detail-select.editing, .detail-textarea.editing {
background-color: #fff;
border-color: #007bff;
color: #333;
}
/* 響應式設計 */
@media (max-width: 768px) {
.patients-container {
padding: 15px;
}
.search-operations-row {
flex-direction: column;
gap: 15px;
align-items: stretch;
}
.search-container {
max-width: 100%;
}
.operations-container {
justify-content: center;
}
.detail-row {
flex-direction: column;
gap: 15px;
}
.detail-group {
width: 100%;
}
.detail-header {
flex-direction: column;
gap: 15px;
align-items: stretch;
}
.header-actions {
justify-content: center;
}
}
/* 小螢幕優化 */
@media (max-width: 480px) {
.search-container {
flex-direction: column;
}
.operations-container {
flex-direction: column;
width: 100%;
}
.operations-container button {
width: 100%;
}
}
/* 錯誤訊息樣式 */
.error-message {
color: #e74c3c;
font-size: 12px;
margin-top: 5px;
display: none;
}
/* 必填欄位標記 */
.required {
color: #e74c3c;
}
/* 錯誤狀態的輸入框 */
.detail-input.error, .detail-select.error {
border-color: #e74c3c !important;
}
/* 編輯模式下的錯誤狀態 */
.detail-input.editing.error, .detail-select.editing.error {
border-color: #e74c3c !important;
box-shadow: 0 0 0 2px rgba(231, 76, 60, 0.2);
}
patients.html
<!-- 使用者登入後會跳轉至此頁面,用於搜尋、檢視、修改和刪除病患資料 -->
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Patients</title>
<link rel="stylesheet" href="style.css">
<link rel="stylesheet" href="patients.css">
</head>
<body>
<nav class="navbar">
<button onclick="window.location.href='index.html'">Login</button>
<!-- 改為Patients按鈕被設為active -->
<button class="active">Patients</button>
<button onclick="window.location.href='records.html'">Records</button>
</nav>
<!-- 主內容區 -->
<div class="patients-container">
<!-- 頂部提醒框 -->
<div class="reminder-box">
<div class="reminder-icon">⚠️</div>
<div class="reminder-text">
</div>
</div>
<!-- 搜尋和操作區 -->
<div class="search-operations-row">
<div class="search-container">
<input type="text" id="searchInput" placeholder="輸入病患姓名、病歷號或身份證字號進行搜尋...">
<button id="searchBtn" class="btn-search">搜尋</button>
</div>
<div class="operations-container">
<button id="refreshBtn" class="btn-refresh">重新整理</button>
<button id="newPatientBtn" class="btn-new">新增病患</button>
<button id="deletePatientBtn" class="btn-delete">刪除病患</button>
</div>
</div>
<!-- 病患詳細資料區塊 -->
<div class="patient-detail-section">
<div class="detail-header">
<h3>病患基本資料</h3>
<div class="header-actions">
<button id="editPatientBtn" class="btn-edit" style="display: none;">編輯資料</button>
<button id="savePatientBtn" class="btn-save" style="display: none;">儲存變更</button>
<button id="cancelEditBtn" class="btn-cancel" style="display: none;">取消編輯</button>
</div>
</div>
<div class="detail-content">
<div class="detail-row">
<div class="detail-group">
<label for="patientName">姓名</label>
<input type="text" id="patientName" class="detail-input" disabled>
<div class="error-message" id="nameError"></div>
</div>
<div class="detail-group">
<label for="patientGender">性別</label>
<select id="patientGender" class="detail-select" disabled>
<option value="">請選擇</option>
<option value="M">男</option>
<option value="F">女</option>
</select>
<div class="error-message" id="genderError"></div>
</div>
<div class="detail-group">
<label for="patientBirthday">生日</label>
<input type="date" id="patientBirthday" class="detail-input" disabled>
<div class="error-message" id="birthdayError"></div>
</div>
</div>
<div class="detail-row">
<div class="detail-group">
<label for="patientIdNumber">身份證字號</label>
<input type="text" id="patientIdNumber" class="detail-input" disabled maxlength="10">
<div class="error-message" id="idNumberError"></div>
</div>
<div class="detail-group">
<label for="patientPhone">連絡電話</label>
<input type="tel" id="patientPhone" class="detail-input" disabled>
<div class="error-message" id="phoneError"></div>
</div>
<div class="detail-group">
<label for="patientBloodType">血型</label>
<select id="patientBloodType" class="detail-select" disabled>
<option value="">請選擇</option>
<option value="A">A型</option>
<option value="B">B型</option>
<option value="O">O型</option>
<option value="AB">AB型</option>
<option value="unknown">未知</option>
</select>
<div class="error-message" id="bloodTypeError"></div>
</div>
</div>
<div class="detail-row">
<div class="detail-group full-width">
<label for="patientAddress">地址</label>
<input type="text" id="patientAddress" class="detail-input" disabled>
<div class="error-message" id="addressError"></div>
</div>
</div>
<div class="detail-row">
<div class="detail-group">
<label for="patientEmergencyContact">緊急連絡人電話</label>
<input type="tel" id="patientEmergencyContact" class="detail-input" disabled>
<div class="error-message" id="emergencyContactError"></div>
</div>
<div class="detail-group">
<label for="patientBadHabits">不良嗜好</label>
<input type="text" id="patientBadHabits" class="detail-input" disabled placeholder="請輸入不良嗜好...">
<div class="error-message" id="badHabitsError"></div>
</div>
<div class="detail-group">
<label for="patientFamilyHistory">家族病史</label>
<input type="text" id="patientFamilyHistory" class="detail-input" disabled placeholder="請輸入家族病史...">
<div class="error-message" id="familyHistoryError"></div>
</div>
</div>
<div class="detail-row">
<div class="detail-group">
<label for="patientMedicalHistory">有無就診記錄</label>
<select id="patientMedicalHistory" class="detail-select" disabled>
<option value="">請選擇</option>
<option value="none">無</option>
<option value="yes">有</option>
</select>
<div class="error-message" id="medicalHistoryError"></div>
</div>
<div class="detail-group">
<label for="patientAllergy">過敏史</label>
<input type="text" id="patientAllergy" class="detail-input" disabled placeholder="請輸入過敏史...">
<div class="error-message" id="allergyError"></div>
</div>
</div>
</div>
</div>
</div>
<script src="script.js"></script>
<script src="patients.js"></script>
</body>
</html>
patients.js
// patients.js - 病患頁面功能(連接真實後端 API)
const API_BASE_URL = 'http://localhost:3001/api';
// 添加全局變數來保存當前病患ID和用戶資訊
let currentPatientId = null;
let currentUser = null;
let currentPatient = null; // 新增:保存完整病患物件
let originalPatientData = null; // 新增:保存原始資料
document.addEventListener('DOMContentLoaded', function() {
// 檢查登入狀態
checkLoginStatus();
// 檢查使用者權限
checkPermissions();
// 綁定事件監聽器
bindEvents();
// 初始化頁面
initializePage();
});
// 檢查登入狀態
function checkLoginStatus() {
const userInfo = localStorage.getItem('userInfo');
if (!userInfo) {
// 如果未登入,跳轉到登入頁面
alert('請先登入系統!');
window.location.href = 'index.html';
return;
}
try {
currentUser = JSON.parse(userInfo);
console.log('✅ 當前使用者:', currentUser);
// 顯示使用者資訊
displayUserInfo();
} catch (error) {
console.error('❌ 解析使用者資訊失敗:', error);
alert('使用者資訊錯誤,請重新登入!');
window.location.href = 'index.html';
}
}
// 顯示使用者資訊在頁面上
function displayUserInfo() {
// 在提醒框中顯示使用者資訊
const reminder = document.querySelector('.reminder-text');
if (reminder && currentUser) {
reminder.innerHTML = `<strong>${currentUser.roleName}您好!</strong>員工ID: ${currentUser.employeeId}`;
}
// 也可以在頁面其他地方顯示使用者資訊
const userInfoElement = document.getElementById('userInfo');
if (userInfoElement && currentUser) {
userInfoElement.innerHTML = `
<div style="display: flex; align-items: center; gap: 15px;">
<span><strong>${currentUser.roleName}</strong> (ID: ${currentUser.employeeId})</span>
<button onclick="logout()" style="padding: 5px 10px; background: #dc3545; color: white; border: none; border-radius: 4px; cursor: pointer;">登出</button>
</div>
`;
}
}
// 檢查使用者權限
function checkPermissions() {
if (!currentUser) {
alert('無法取得使用者權限,請重新登入!');
window.location.href = 'index.html';
return;
}
console.log('🔑 當前使用者權限:', currentUser.role);
// 隱藏新增按鈕
document.getElementById('newPatientBtn').style.display = 'none';
// 根據使用者角色設置權限
if (currentUser.role === 'doctor') {
document.getElementById('deletePatientBtn').style.display = 'block';
} else {
document.getElementById('deletePatientBtn').style.display = 'none';
}
// 顯示權限提示
showPermissionMessage();
}
// 顯示權限提示
function showPermissionMessage() {
const reminder = document.querySelector('.reminder-text');
if (!reminder || !currentUser) return;
let permissionText = '';
switch(currentUser.role) {
case 'doctor':
permissionText = '您的權限為查詢/修改/刪除資料!';
break;
case 'therapist':
permissionText = '您的權限為查詢/修改資料!';
break;
case 'nurse':
case 'pharmacist':
permissionText = '您的權限僅為查詢!';
break;
default:
permissionText = '您的權限僅為查詢!';
}
// 更新提醒框內容,包含權限資訊
reminder.innerHTML = `
<strong>${currentUser.roleName}您好!</strong>
<span style="margin-left: 10px;">員工ID: ${currentUser.employeeId}</span>
<br>
<span style="color: #666; font-size: 14px;">${permissionText}</span>
`;
}
// 登出功能
function logout() {
if (confirm('確定要登出系統嗎?')) {
localStorage.removeItem('userInfo');
window.location.href = 'index.html';
}
}
// 綁定事件監聽器
function bindEvents() {
// 搜尋按鈕
document.getElementById('searchBtn').addEventListener('click', searchPatient);
// 重新整理按鈕
document.getElementById('refreshBtn').addEventListener('click', refreshPage);
// 編輯相關按鈕
document.getElementById('editPatientBtn').addEventListener('click', enableEditing);
document.getElementById('savePatientBtn').addEventListener('click', savePatient);
document.getElementById('cancelEditBtn').addEventListener('click', cancelEditing);
// 刪除病患按鈕
document.getElementById('deletePatientBtn').addEventListener('click', deletePatient);
// 搜尋輸入框按 Enter 鍵搜尋
document.getElementById('searchInput').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
searchPatient();
}
});
}
// 搜尋病患 - 連接真實後端 API
async function searchPatient() {
const searchTerm = document.getElementById('searchInput').value.trim();
if (!searchTerm) {
alert('請輸入搜尋條件!');
return;
}
showLoading(true);
try {
const apiUrl = `${API_BASE_URL}/patients/search?query=${encodeURIComponent(searchTerm)}`;
const response = await fetch(apiUrl);
const data = await response.json();
if (!response.ok) throw new Error(data.error || '搜尋失敗');
if (!data.patients || data.patients.length === 0) {
alert('找不到符合條件的病患!');
refreshPage(); // 找不到資料時也重置頁面
return;
}
const patient = data.patients[0];
// 保存完整病患物件
currentPatient = patient;
currentPatientId = patient.PATIENT_ID;
console.log('💾 保存當前病患:', currentPatient);
fillPatientForm(patient);
originalPatientData = getFormData();
// 根據權限顯示按鈕
document.getElementById('editPatientBtn').style.display = (currentUser.role === 'doctor' || currentUser.role === 'therapist') ? 'block' : 'none';
document.getElementById('deletePatientBtn').style.display = (currentUser.role === 'doctor') ? 'block' : 'none';
alert(`找到病患:${patient.NAME}`);
} catch (error) {
console.error('❌ 搜尋失敗:', error);
alert('搜尋失敗:' + error.message);
refreshPage();
} finally {
showLoading(false);
}
}
// 重新整理頁面
function refreshPage() {
// 清空搜尋框和表單
document.getElementById('searchInput').value = '';
clearPatientForm();
// 隱藏所有按鈕
document.getElementById('editPatientBtn').style.display = 'none';
document.getElementById('savePatientBtn').style.display = 'none';
document.getElementById('cancelEditBtn').style.display = 'none';
document.getElementById('deletePatientBtn').style.display = 'none';
document.getElementById('newPatientBtn').style.display = 'none';
// 重置當前病患ID
currentPatientId = null;
currentPatient = null;
// 清空原始資料
originalPatientData = null;
// 清除錯誤訊息
clearAllErrors();
alert('頁面已重新整理!');
}
// 初始化頁面
function initializePage() {
// 確保所有按鈕在初始狀態是隱藏的
document.getElementById('editPatientBtn').style.display = 'none';
document.getElementById('savePatientBtn').style.display = 'none';
document.getElementById('cancelEditBtn').style.display = 'none';
document.getElementById('deletePatientBtn').style.display = 'none';
document.getElementById('newPatientBtn').style.display = 'none';
}
// 啟用編輯模式
function enableEditing() {
// 檢查權限
if (currentUser.role !== 'doctor' && currentUser.role !== 'therapist') {
alert('您沒有編輯病患資料的權限!');
return;
}
// 確保有病患ID才能編輯
if (!currentPatientId) {
alert('請先搜尋要編輯的病患!');
return;
}
const inputs = document.querySelectorAll('.detail-input, .detail-select');
inputs.forEach(input => {
input.disabled = false;
input.classList.add('editing');
});
// 顯示儲存和取消按鈕,隱藏編輯和刪除按鈕
document.getElementById('editPatientBtn').style.display = 'none';
document.getElementById('savePatientBtn').style.display = 'block';
document.getElementById('cancelEditBtn').style.display = 'block';
document.getElementById('deletePatientBtn').style.display = 'none';
}
// 取得表單資料的函數
function getFormData() {
return {
name: document.getElementById('patientName').value,
gender: document.getElementById('patientGender').value,
birthday: document.getElementById('patientBirthday').value,
id_number: document.getElementById('patientIdNumber').value,
phone: document.getElementById('patientPhone').value,
blood_type: document.getElementById('patientBloodType').value,
address: document.getElementById('patientAddress').value,
emergency_contact: document.getElementById('patientEmergencyContact').value,
bad_habits: document.getElementById('patientBadHabits').value,
family_history: document.getElementById('patientFamilyHistory').value,
allergy: document.getElementById('patientAllergy').value
};
}
// 取消編輯
function cancelEditing() {
// 🆕 恢復原始資料
if (originalPatientData) {
console.log('🔄 恢復原始資料:', originalPatientData);
restoreFormData(originalPatientData);
originalPatientData = null; // 清空保存的資料
}
const inputs = document.querySelectorAll('.detail-input, .detail-select');
inputs.forEach(input => {
input.disabled = true;
input.classList.remove('editing');
});
// 根據權限顯示編輯按鈕
if ((currentUser.role === 'doctor' || currentUser.role === 'therapist') && currentPatientId) {
document.getElementById('editPatientBtn').style.display = 'block';
} else {
document.getElementById('editPatientBtn').style.display = 'none';
}
document.getElementById('savePatientBtn').style.display = 'none';
document.getElementById('cancelEditBtn').style.display = 'none';
// 根據權限顯示刪除按鈕
if (currentUser.role === 'doctor' && currentPatientId) {
document.getElementById('deletePatientBtn').style.display = 'block';
} else {
document.getElementById('deletePatientBtn').style.display = 'none';
}
// 清除錯誤訊息
clearAllErrors();
}
// 恢復表單資料的函數
function restoreFormData(data) {
document.getElementById('patientName').value = data.name || '';
document.getElementById('patientGender').value = data.gender || '';
document.getElementById('patientBirthday').value = data.birthday || '';
document.getElementById('patientIdNumber').value = data.id_number || '';
document.getElementById('patientPhone').value = data.phone || '';
document.getElementById('patientBloodType').value = data.blood_type || '';
document.getElementById('patientAddress').value = data.address || '';
document.getElementById('patientEmergencyContact').value = data.emergency_contact || '';
document.getElementById('patientBadHabits').value = data.bad_habits || '';
document.getElementById('patientFamilyHistory').value = data.family_history || '';
document.getElementById('patientAllergy').value = data.allergy || '';
}
// 清空病患表單
function clearPatientForm() {
document.getElementById('patientName').value = '';
document.getElementById('patientGender').value = '';
document.getElementById('patientBirthday').value = '';
document.getElementById('patientIdNumber').value = '';
document.getElementById('patientPhone').value = '';
document.getElementById('patientBloodType').value = '';
document.getElementById('patientAddress').value = '';
document.getElementById('patientEmergencyContact').value = '';
document.getElementById('patientBadHabits').value = '';
document.getElementById('patientFamilyHistory').value = '';
document.getElementById('patientMedicalHistory').value = '';
document.getElementById('patientAllergy').value = '';
}
// 填入病患表單 - 處理 null 值
function fillPatientForm(patientData) {
console.log('📝 填入表單資料:', patientData);
// PATIENTS 表資料
document.getElementById('patientName').value = patientData.NAME || '';
document.getElementById('patientGender').value = (patientData.GENDER || '').trim();
document.getElementById('patientBirthday').value = patientData.BIRTHDAY || '';
document.getElementById('patientIdNumber').value = patientData.ID_NUMBER || '';
document.getElementById('patientPhone').value = patientData.PHONE || '';
document.getElementById('patientBloodType').value = patientData.BLOOD_TYPE || '';
document.getElementById('patientAddress').value = patientData.ADDRESS || '';
document.getElementById('patientEmergencyContact').value = patientData.EMERGENCY_CONTACT || '';
// RECORDS 表資料 - 處理 null 值
document.getElementById('patientBadHabits').value = patientData.BAD_HABITS || '(無資料)';
document.getElementById('patientFamilyHistory').value = patientData.FAMILY_HISTORY || '(無資料)';
document.getElementById('patientMedicalHistory').value = patientData.MEDICAL_HISTORY || 'none';
document.getElementById('patientAllergy').value = patientData.ALLERGY || '(無資料)';
console.log('✅ 表單填入完成');
}
// 儲存病患資料
async function savePatient() {
// 驗證必填欄位
if (!validateForm()) {
return;
}
// 確保有病患ID
if (!currentPatientId) {
alert('請先搜尋要編輯的病患!');
return;
}
// 檢查權限
if (currentUser.role !== 'doctor' && currentUser.role !== 'therapist') {
alert('您沒有修改病患資料的權限!');
return;
}
await updateExistingPatient();
}
// 更新現有病患資料
async function updateExistingPatient() {
const patientData = getFormData(); // 使用統一的函數取得資料
const patientId = currentPatientId;
console.log('💾 準備更新病患資料,ID:', patientId);
console.log('📦 更新資料:', patientData);
// 顯示載入中狀態
showLoading(true);
try {
// 使用 PUT 方法
const apiUrl = `${API_BASE_URL}/patients/${patientId}`;
console.log('🌐 API 網址:', apiUrl);
const response = await fetch(apiUrl, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(patientData)
});
console.log('📡 回應狀態:', response.status, response.statusText);
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || `HTTP ${response.status}: ${response.statusText}`);
}
console.log('✅ 更新成功:', data);
alert('病患資料已更新成功!');
// 清空原始資料
originalPatientData = null;
// 回到檢視模式
cancelEditing();
} catch (error) {
console.error('❌ 更新失敗:', error);
alert('更新失敗:' + error.message);
} finally {
showLoading(false);
}
}
// 更新現有病患資料
async function updateExistingPatient() {
const patientData = {
name: document.getElementById('patientName').value,
gender: document.getElementById('patientGender').value,
birthday: document.getElementById('patientBirthday').value,
id_number: document.getElementById('patientIdNumber').value,
phone: document.getElementById('patientPhone').value,
blood_type: document.getElementById('patientBloodType').value,
address: document.getElementById('patientAddress').value,
emergency_contact: document.getElementById('patientEmergencyContact').value,
bad_habits: document.getElementById('patientBadHabits').value,
family_history: document.getElementById('patientFamilyHistory').value,
allergy: document.getElementById('patientAllergy').value
};
const patientId = currentPatientId;
console.log('💾 準備更新病患資料,ID:', patientId);
console.log('📦 更新資料:', patientData);
// 顯示載入中狀態
showLoading(true);
try {
// 使用 PUT 方法
const apiUrl = `${API_BASE_URL}/patients/${patientId}`;
console.log('🌐 API 網址:', apiUrl);
const response = await fetch(apiUrl, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(patientData)
});
console.log('📡 回應狀態:', response.status, response.statusText);
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || `HTTP ${response.status}: ${response.statusText}`);
}
console.log('✅ 更新成功:', data);
alert('病患資料已更新成功!');
// 回到檢視模式
cancelEditing();
} catch (error) {
console.error('❌ 更新失敗:', error);
alert('更新失敗:' + error.message);
} finally {
showLoading(false);
}
}
// 刪除病患
async function deletePatient() {
// 步驟 1: 檢查權限和當前狀態
if (!currentUser || currentUser.role !== 'doctor') {
alert('您的權限不足,無法刪除病患!');
return;
}
if (!currentPatientId || !currentPatient) {
alert('請先搜尋到要刪除的病患!');
return;
}
// 步驟 2: 顯示一個內容更詳細、更安全的警告確認視窗
const confirmation = confirm(
`您確定要刪除病患 「${currentPatient.NAME}」 (ID: ${currentPatientId}) 嗎?\n\n` +
`🚨 警告:此操作將會永久刪除該病患的所有相關病歷資料 (包含住院、手術、過敏等所有紀錄),且無法復原!`
);
if (!confirmation) {
console.log('使用者取消了刪除操作。');
return; // 如果使用者按下「取消」,則停止執行
}
console.log(`準備刪除病患 ID: ${currentPatientId}`);
showLoading(true);
try {
// 步驟 3: 發送 DELETE 請求到後端
const apiUrl = `${API_BASE_URL}/patients/${currentPatientId}`;
console.log('🌐 發送 DELETE 請求到:', apiUrl);
const response = await fetch(apiUrl, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
}
});
const data = await response.json();
console.log('📨 後端回應:', data);
if (!response.ok) {
throw new Error(data.error || `刪除失敗: ${response.statusText}`);
}
console.log('✅ 刪除成功:', data);
alert(`病患 「${currentPatient.NAME}」 及其所有相關資料已成功刪除!`);
// 步驟 4: 刪除成功後,呼叫 refreshPage() 將頁面完全重置
refreshPage();
} catch (error) {
console.error('❌ 刪除失敗:', error);
alert('刪除失敗:' + error.message);
} finally {
showLoading(false);
}
}
// 表單驗證
function validateForm() {
const name = document.getElementById('patientName').value.trim();
const gender = document.getElementById('patientGender').value;
const birthday = document.getElementById('patientBirthday').value;
const idNumber = document.getElementById('patientIdNumber').value.trim();
if (!name) {
alert('請填寫病患姓名!');
document.getElementById('patientName').focus();
return false;
}
if (!gender) {
alert('請選擇性別!');
document.getElementById('patientGender').focus();
return false;
}
if (!birthday) {
alert('請選擇生日!');
document.getElementById('patientBirthday').focus();
return false;
}
if (!idNumber) {
alert('請填寫身份證字號!');
document.getElementById('patientIdNumber').focus();
return false;
}
return true;
}
// 顯示/隱藏載入中狀態
function showLoading(show) {
if (show) {
let loadingOverlay = document.getElementById('loadingOverlay');
if (!loadingOverlay) {
loadingOverlay = document.createElement('div');
loadingOverlay.id = 'loadingOverlay';
loadingOverlay.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
color: white;
font-size: 18px;
`;
loadingOverlay.innerHTML = `
<div style="background: white; padding: 30px; border-radius: 10px; color: #333; text-align: center;">
<div style="margin-bottom: 15px;">處理中...</div>
<div style="width: 40px; height: 40px; border: 4px solid #f3f3f3; border-top: 4px solid #007bff; border-radius: 50%; animation: spin 1s linear infinite; margin: 0 auto;"></div>
</div>
`;
document.body.appendChild(loadingOverlay);
const style = document.createElement('style');
style.textContent = `
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
`;
document.head.appendChild(style);
}
loadingOverlay.style.display = 'flex';
} else {
const loadingOverlay = document.getElementById('loadingOverlay');
if (loadingOverlay) {
loadingOverlay.style.display = 'none';
}
}
}
// 清除所有錯誤訊息
function clearAllErrors() {
const errorElements = document.querySelectorAll('.error-message');
errorElements.forEach(element => {
element.textContent = '';
element.style.display = 'none';
});
const fields = document.querySelectorAll('.detail-input, .detail-select');
fields.forEach(field => {
field.style.borderColor = '#ddd';
});
}
records.html
<!-- 此頁面專門用來檢視特定病患的詳細醫療記錄(手術、住院、過敏、檢驗檢查記錄) -->
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Records</title>
<link rel="stylesheet" href="style.css">
<link rel="stylesheet" href="records.css">
</head>
<body>
<nav class="navbar">
<button onclick="window.location.href='index.html'">Login</button>
<button onclick="window.location.href='patients.html'">Patients</button>
<button classs="active">Records</button>
<button onclick="window.location.href='schedules.html'">Schedules</button>
<button onclick="window.location.href='resources.html'">MedicalResourceUsage</button>
</nav>
<div class="records-container">
<section class="patient-header-section">
<div class="patient-unified-card">
<div class="search-area">
<h4>查詢病患</h4>
<div class="search-input-group">
<input type="text" id="patientSearchInput" placeholder="輸入病歷號或姓名...">
<button id="searchPatientBtn">查詢</button>
</div>
</div>
<hr class="card-divider">
<div class="info-area">
<div class="avatar-placeholder"></div>
<h3 id="patientNameDisplay">請先查詢病患</h3>
<p id="patientIdDisplay">Patient ID: ---</p>
</div>
</div>
</section>
<section class="records-section">
<div class="records-grid">
<div class="record-card">
<div class="card-header"><h4>手術記錄</h4></div>
<div class="card-body">
<div class="form-group">
<label for="surgeryName">手術名稱</label>
<textarea id="surgeryName" rows="1" disabled></textarea>
</div>
<div class="form-group">
<label for="surgeryPart">手術部位</label>
<textarea id="surgeryPart" rows="1" disabled></textarea>
</div>
<div class="form-group">
<label for="surgerySuggestion">手術建議</label>
<textarea id="surgerySuggestion" rows="2" disabled></textarea>
</div>
<div class="form-group">
<label for="surgeryComplications">併發症</label>
<textarea id="surgeryComplications" rows="2" disabled></textarea>
</div>
</div>
</div>
<div class="record-card">
<div class="card-header"><h4>住院記錄</h4></div>
<div class="card-body grid-2-col">
<div class="form-group"><label for="hospitalWard">病房名</label><input type="text" id="hospitalWard" disabled></div>
<div class="form-group"><label for="hospitalBedName">病床名</label><input type="text" id="hospitalBedName" disabled></div>
<div class="form-group"><label for="hospitalBedNumber">病床號</label><input type="text" id="hospitalBedNumber" disabled></div>
<div class="form-group"><label for="hospitalStayDays">住院天數</label><input type="text" id="hospitalStayDays" disabled></div>
<div class="form-group"><label for="admissionDate">住院日期</label><input type="text" id="admissionDate" placeholder="YYYY-MM-DD" disabled></div>
<div class="form-group"><label for="dischargeDate">出院日期</label><input type="text" id="dischargeDate" placeholder="YYYY-MM-DD" disabled></div>
<div class="form-group full-width"><label for="admissionReason">住院原因</label><input type="text" id="admissionReason" disabled></div>
<div class="form-group full-width"><label for="dischargeReason">出院原因</label><input type="text" id="dischargeReason" disabled></div>
</div>
</div>
<div class="record-card">
<div class="card-header"><h4>過敏記錄</h4></div>
<div class="card-body">
<div class="form-group">
<label for="allergen">過敏原</label>
<textarea id="allergen" rows="1" disabled></textarea>
</div>
<div class="form-group">
<label for="allergySymptom">症狀</label>
<textarea id="allergySymptom" rows="1" disabled></textarea>
</div>
<div class="form-group">
<label for="allergySeverity">嚴重程度</label>
<textarea id="allergySeverity" rows="1" disabled></textarea>
</div>
<div class="form-group">
<label for="allergyNotes">備註</label>
<textarea id="allergyNotes" rows="2" disabled></textarea>
</div>
</div>
</div>
<div class="record-card">
<div class="card-header"><h4>檢驗檢查紀錄</h4></div>
<div class="card-body">
<div class="form-group">
<label for="labTestName">檢驗項目</label>
<textarea id="labTestName" rows="1" disabled></textarea>
</div>
<div class="form-group">
<label for="labTestDate">檢驗日期</label>
<input type="text" id="labTestDate" placeholder="YYYY-MM-DD" disabled>
</div>
<div class="form-group">
<label for="labTestResult">數值/結果</label>
<textarea id="labTestResult" rows="1" disabled></textarea>
</div>
<div class="form-group">
<label for="labTestSuggestion">醫師建議</label>
<textarea id="labTestSuggestion" rows="2" disabled></textarea>
</div>
</div>
</div>
</div>
</section>
</div>
<script src="records.js"></script>
</body>
</html>
records.css
/* records.css - 更新按鈕與樣式版本 */
body { font-family: 'Segoe UI', 'Microsoft JhengHei', Arial, sans-serif; margin: 0; background-color: #F4F6F8; }
.navbar { background-color: #a3d5e4; padding: 15px 10px; display: flex; justify-content: center; gap: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.navbar button { background: white; border: none; border-radius: 8px; padding: 10px 25px; color: #333; font-size: 14px; font-weight: bold; cursor: pointer; transition: all 0.2s ease-in-out; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
.navbar button:hover { transform: translateY(-2px); box-shadow: 0 4px 8px rgba(0,0,0,0.15); }
.navbar button.active { background-color: #e9ecef; color: #007bff; box-shadow: inset 0 2px 4px rgba(0,0,0,0.1); }
.records-container { display: flex; flex-direction: column; align-items: center; gap: 30px; padding: 30px; }
.patient-header-section { display: flex; justify-content: center; width: 100%; }
.patient-unified-card { background: white; border-radius: 8px; padding: 24px; box-shadow: 0 2px 10px rgba(0,0,0,0.07); border: 1px solid #EAECEE; width: 100%; max-width: 700px; }
.search-area h4 { margin-top: 0; margin-bottom: 16px; color: #34495E; }
.search-input-group { display: flex; }
.search-input-group input { flex-grow: 1; border: 1px solid #D5D8DC; border-radius: 6px 0 0 6px; padding: 10px; font-size: 14px; }
.search-input-group button { padding: 10px 16px; border: none; background-color: #007bff; color: white; border-radius: 0 6px 6px 0; cursor: pointer; }
.card-divider { border: none; border-top: 1px solid #EAECEE; margin: 24px 0; }
.info-area { text-align: center; }
.avatar-placeholder { width: 80px; height: 80px; border-radius: 50%; background-color: #EAECEE; margin: 0 auto 15px; }
#patientNameDisplay { margin: 10px 0 5px; color: #2C3E50; font-size: 1.1em; font-weight: bold; }
#patientIdDisplay { margin: 0; color: #808B96; font-size: 14px; }
.records-section { width: 100%; max-width: 1800px; }
.records-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; width: 100%; }
.record-card { background: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.07); border: 1px solid #EAECEE; display: flex; flex-direction: column; min-width: 0; }
.card-header { background: #a3d5e4; padding: 12px 16px; border-bottom: 1px solid #90C4D5; border-radius: 8px 8px 0 0; }
.card-header h4 { margin: 0; color: #333; font-size: 0.95em; font-weight: bold; white-space: nowrap; }
.card-body { padding: 16px; display: flex; flex-direction: column; gap: 12px; }
.card-body.grid-2-col { display: grid; grid-template-columns: repeat(2, minmax(